/*
 * server_test.cpp
 *
 *  Created on: 10/06/2011
 *      Author: Itay A. & Vova D.
 */


#include "calculation_server.h"
#include "Polynomial.h"
#include "SolveEquationSystem.h"
#include "Similarity.h"
#include "mtmtest.h"
#include "matrix.h"
#include <stdarg.h>


Matrix initMatrix( int rows, int columns, ... )
{
Matrix result( rows, columns );


va_list vl;
va_start( vl, columns );


for( int r = 1; r <= rows; ++r )
{
for( int c = 1; c <= columns; ++c )
{
double cell = va_arg(vl,double);
result(r, c) = cell;
}
}


va_end( vl );


return result;
}


bool testAddJob(){
CalculationServer server;
// init two matrices
Matrix A=initMatrix(2,2,1.5,2.5,3.5,4.5);
Matrix B=initMatrix(2,2,1.5,5.5,6.5,8.5);


// add legal jobs
int job1,job2,job3,job4,job5,job6,job7,job8;
ASSERT_NO_THROW(job1 = server.addJob
(CalculationServer::SOLVE_EQUATION_SYSTEM,50,A,B));
ASSERT_NO_THROW(job2 = server.addJob(CalculationServer::SIMILARITY,30,A,B));
ASSERT_NO_THROW(job3 = server.addJob(CalculationServer::POLYNOMIAL,20,A,B));
// verify all ID's are different
ASSERT_EQUALS(job1!=job2, true);
ASSERT_EQUALS(job2!=job3, true);
ASSERT_EQUALS(job1!=job3, true);
// try to add with illegal priority
ASSERT_THROW(CalculationServer::InvalidPriority,
server.addJob(CalculationServer::SIMILARITY,-1,A,B));
ASSERT_THROW(CalculationServer::InvalidPriority,
server.addJob(CalculationServer::POLYNOMIAL,101,A,B));
ASSERT_THROW(CalculationServer::InvalidPriority,
server.addJob(CalculationServer::SOLVE_EQUATION_SYSTEM,200,A,B));
// check the order of jobs is correct
ASSERT_EQUALS(job1,server.nextJob());
ASSERT_NO_THROW(job4 = server.addJob
(CalculationServer::SOLVE_EQUATION_SYSTEM,51,A,B));
ASSERT_EQUALS(job4,server.nextJob());
ASSERT_NO_THROW(job5 = server.addJob(CalculationServer::POLYNOMIAL,50,A,B));
ASSERT_EQUALS(job4,server.nextJob());
// check that it's possible to insert similar jobs
ASSERT_NO_THROW(job6 = server.addJob
(CalculationServer::SOLVE_EQUATION_SYSTEM,50,A,B));
ASSERT_NO_THROW(job7 = server.addJob(CalculationServer::SIMILARITY,30,A,B));
ASSERT_NO_THROW(job8 = server.addJob(CalculationServer::POLYNOMIAL,20,A,B));
// verify their ID's are different
ASSERT_EQUALS(job1!=job6, true);
ASSERT_EQUALS(job2!=job7, true);
ASSERT_EQUALS(job1!=job8, true);


return true;
}


bool testRemoveJob(){
CalculationServer server;
    Matrix A = initMatrix(2,2, 1.0, 1.0, 1.0, 0.0);
    Matrix B = initMatrix(4,1, 2.0, 0.0, 1.0, -1.0);
    //Try removing not existing jobs on empty list
    ASSERT_THROW(CalculationServer::JobNotFound, server.removeJob( -1));
    ASSERT_THROW(CalculationServer::JobNotFound, server.removeJob( 15));
    // Add jobs
    int id0 = server.addJob(CalculationServer::POLYNOMIAL, 50, A, B);
    int id1 = server.addJob(CalculationServer::SOLVE_EQUATION_SYSTEM, 50, Matrix::createUnit(4), B);
    int id2 = server.addJob(CalculationServer::SIMILARITY, 80, A, A);
    // Jobs order should be: id2 -> id0 -> id1


    //remove last job
    ASSERT_NO_THROW(server.removeJob(id1));
    ASSERT_EQUALS(server.totalJobs(),2);
    ASSERT_THROW(CalculationServer::JobNotFound, server.removeJob( id1));
    //Check that The order hasn't changed
    ASSERT_EQUALS(id2, server.nextJob());


    //remove first job
    ASSERT_NO_THROW(server.removeJob(id2));
    ASSERT_EQUALS(server.totalJobs(),1);
    ASSERT_THROW(CalculationServer::JobNotFound, server.removeJob( id2));
    ASSERT_EQUALS(id0, server.nextJob());


    //remove the only job left
    ASSERT_NO_THROW(server.removeJob(id0));
    ASSERT_EQUALS(server.totalJobs(),0);
    ASSERT_THROW(CalculationServer::JobNotFound, server.removeJob( id0));
    //No jobs left
    ASSERT_THROW(CalculationServer::JobNotFound, server.nextJob());


    return true;
}


bool testExecute(){
    CalculationServer server;
    Matrix A = initMatrix(2,2, 1.0, 1.0, 1.0, 0.0);
    Matrix B = initMatrix(4,1, 2.0, 0.0, 1.0, -1.0);
    //Execute when no jobs are in queue
    ASSERT_THROW(CalculationServer::JobNotFound, server.execute());
    // Add jobs
    int id0 = server.addJob(CalculationServer::POLYNOMIAL, 50, A, B);
    int id1 = server.addJob(CalculationServer::SOLVE_EQUATION_SYSTEM, 50, Matrix::createUnit(4), B);
    int id2 = server.addJob(CalculationServer::SIMILARITY, 80, A, A);
    // Jobs order should be: id2 -> id0 -> id1


    //executeed job should be id2
    ASSERT_NO_THROW(server.execute());
    ASSERT_EQUALS(server.totalJobs(),2);
    ASSERT_THROW(CalculationServer::JobNotFound, server.removeJob( id2));
    //Check that The order hasn't changed, and that it s correct
    ASSERT_EQUALS(id0, server.nextJob());


    //Add a new job with same priority as id0
    int id3 = server.addJob(CalculationServer::SIMILARITY, 50, A, Matrix::createUnit(5));
    Matrix output = server.execute();  //execute job id0
    Matrix C = initMatrix(2,2, 1.0, -1.0, -1.0, 2.0);
    ASSERT_EQUALS(C, output);
    ASSERT_EQUALS(server.totalJobs(),2);
    ASSERT_THROW(CalculationServer::JobNotFound, server.removeJob( id0));
    ASSERT_EQUALS(id1, server.nextJob());


    //change priority and execute.
    server.changePriority(id1,20);
    //now executed job shoudl be id3, which should throw
    ASSERT_THROW(CalculationServer::OperationFailed, server.execute());
    ASSERT_EQUALS(server.totalJobs(),1);
    ASSERT_THROW(CalculationServer::JobNotFound, server.removeJob( id3));
    ASSERT_EQUALS(id1, server.nextJob());


    //execute last job
    ASSERT_NO_THROW(server.execute());
    ASSERT_EQUALS(server.totalJobs(),0);
    ASSERT_THROW(CalculationServer::JobNotFound, server.removeJob( id1));
    //no more jobs!
    ASSERT_THROW(CalculationServer::JobNotFound, server.execute());


    return true;


}


bool testTotalJobs(){
    CalculationServer server;


    Matrix A = initMatrix(2,2, 1.0, 1.0, 1.0, 0.0);
    Matrix B = initMatrix(4,1, 2.0, 0.0, 1.0, -1.0);
    //number of jobs is 0
    ASSERT_EQUALS(0, server.totalJobs());


    // Add jobs
    server.addJob(CalculationServer::POLYNOMIAL, 50, A, B);
    ASSERT_EQUALS(1, server.totalJobs()); // 1
    int id1 = server.addJob(CalculationServer::SOLVE_EQUATION_SYSTEM, 50, Matrix::createUnit(4), B);
    ASSERT_EQUALS(2, server.totalJobs()); // 2
    server.addJob(CalculationServer::SIMILARITY, 80, A, A);
    ASSERT_EQUALS(3, server.totalJobs()); // 3


    //removed a job
    ASSERT_NO_THROW(server.removeJob(id1));
    ASSERT_EQUALS(2, server.totalJobs()); // 2
    //execute a job
    ASSERT_NO_THROW(server.execute());
    ASSERT_EQUALS(1, server.totalJobs()); // 1
    //execute last job, 0 again...
    ASSERT_NO_THROW(server.execute());
    ASSERT_EQUALS(0, server.totalJobs()); // 0


    return true;


}


bool testChangePriority(){
    CalculationServer server;


    Matrix A = initMatrix(2,2, 1.0, 1.0, 1.0, 0.0);
    Matrix B = initMatrix(4,1, 2.0, 0.0, 1.0, -1.0);
    // Add jobs
    int id0 = server.addJob(CalculationServer::POLYNOMIAL, 50, A, B);
    int id1 = server.addJob(CalculationServer::SOLVE_EQUATION_SYSTEM, 50, Matrix::createUnit(4), B);
    int id2 = server.addJob(CalculationServer::SIMILARITY, 80, A, A);


    // Jobs order should be: id2 -> id0 -> id1
    ASSERT_EQUALS(id2, server.nextJob());


    // The following error doesn't change the server's queue:
    ASSERT_THROW(CalculationServer::InvalidPriority, server.changePriority(id2, -10));
    ASSERT_EQUALS(id2, server.nextJob());


    //put id2 at end of queue
     ASSERT_NO_THROW(server.changePriority(id2,30)); //  id0 -> id1 -> id2


     //Now id2 is the last in queue, and id0 is first
    ASSERT_EQUALS(id0, server.nextJob());


    //move id1 to top of queue
    ASSERT_NO_THROW(server.changePriority(id1,100)); // id1 -> id0 -> id2
    ASSERT_EQUALS(id1, server.nextJob());


    //give id2 the same priority as id1.
    ASSERT_NO_THROW(server.changePriority(id2,100)); // id1 -> id2 -> id0
    ASSERT_EQUALS(id1, server.nextJob()); //id1 is still first cause it's been entered first


    //set id0 priority also to 100, which should put it at the top of the list
    ASSERT_NO_THROW(server.changePriority(id0,100)); // id0 -> id1 -> id2
    ASSERT_EQUALS(id0, server.nextJob()); //id0 is First


    //execute all jobs to verify the order
    server.execute(); // id1 -> id2
    ASSERT_EQUALS(id1, server.nextJob()); //id1 is First
    server.execute(); // id2
    ASSERT_EQUALS(id2, server.nextJob()); //id2 is First


    //Test throwing...


    //Illegal job
    ASSERT_THROW(CalculationServer::JobNotFound,server.changePriority(1000,100));
    //Illegal Priority
    ASSERT_THROW(CalculationServer::InvalidPriority,server.changePriority(id2,-1));
    //Both
    ASSERT_THROW(CalculationServer::JobNotFound,server.changePriority(150,-1));
    //0 is a legal priority!
    ASSERT_NO_THROW(server.changePriority(id2,0));


    return true;


}


bool testNextJob(){
    CalculationServer server;


    Matrix A = initMatrix(2,2, 1.0, 1.0, 1.0, 0.0);
    Matrix B = initMatrix(4,1, 2.0, 0.0, 1.0, -1.0);


    //No next job, should throw.
    ASSERT_THROW(CalculationServer::JobNotFound,server.nextJob());


    // Add jobs
    int id0 = server.addJob(CalculationServer::POLYNOMIAL, 50, A, B);
    ASSERT_EQUALS(id0,server.nextJob());
    int id1 = server.addJob(CalculationServer::SOLVE_EQUATION_SYSTEM, 50, Matrix::createUnit(4), B);
    ASSERT_EQUALS(id0,server.nextJob());
    int id2 = server.addJob(CalculationServer::SIMILARITY, 80, A, A);
    ASSERT_EQUALS(id2,server.nextJob());
    // Jobs order should be: id2 -> id0 -> id1


    //nextJob after execution
    server.execute();
    ASSERT_EQUALS(id0,server.nextJob()); // id0 -> id1
    //next job after changing priority
    ASSERT_NO_THROW(server.changePriority(id0,10));
    ASSERT_EQUALS(id1,server.nextJob()); // id1 -> id0
    //after deleting
    ASSERT_NO_THROW(server.removeJob(id1));
    ASSERT_EQUALS(id0,server.nextJob()); // id0
    //Delete All
    ASSERT_NO_THROW(server.execute());
    ASSERT_THROW(CalculationServer::JobNotFound,server.nextJob());


    return true;
}


/*bool testPoly(){
// init matrices
Matrix A = initMatrix(2,2,1.5,2.5,3.5,4.5);
Matrix B = initMatrix(2,1,5.5,6.5);
Matrix C = initMatrix(4,1,5.5,8.5,-1.0,5.0);
// test creation
Polynomial poly1(A,B,5);
Polynomial poly2(A,C,25);
Matrix outputAB = initMatrix(2,2,15.25,16.25,22.75,34.75);
Matrix outputAC = initMatrix(2,2,352.25,481.25,673.75,929.75);


// test execute
ASSERT_NO_THROW(poly1.execute());
Matrix AB(poly1.execute());
ASSERT_EQUALS(AB == outputAB, true);
ASSERT_NO_THROW(poly2.execute());
Matrix AC(poly2.execute());
ASSERT_EQUALS(AC == outputAC, true);


// test illegal
Matrix D = initMatrix(2,2,1.5,1.5,1.5,2.5);
Matrix E = initMatrix(1,4,2.5,2.5,2.5,3.5);
Poly poly3(A,D,56);
Poly poly4(A,E,41);
// try to execute them, should fail
ASSERT_THROW(CalculationServer::OperationFailed,poly3.execute());
ASSERT_THROW(CalculationServer::OperationFailed,poly4.execute());


return true;
}


bool testSimilarity(){
// init matrices
Matrix A = initMatrix(2,2,1.5,2.5,3.5,4.5);
Matrix B = initMatrix(2,2,1.0,2.0,1.0,2.0);
Matrix C = initMatrix(3,3,1.0,-2.2,3.5,0.33,2.1,22.0,-9.0,5.0,2.5);
Matrix D = initMatrix(3,3,0.0,0.0,2.5,3.0,2.0,5.0,0.0,0.0,5.5);
// test creation
Similarity sim1(A,B,100);
Similarity sim2(C,D,22);
Matrix outputAB = initMatrix(2,2,-8.5,-11.5,8.5,11.5);
Matrix outputCD = initMatrix(3,3,
10.296052794186700, -5.740873476853130,  0.295978645048072,
10.133295434884700, -5.660421661435050,  1.850577127462380,
-3.000800810697240,  1.653698806198860,  2.864368867248330);


// test execute
ASSERT_NO_THROW(sim1.execute());
Matrix AB(sim1.execute());
ASSERT_EQUALS(AB == outputAB, true);
ASSERT_NO_THROW(sim2.execute());
Matrix CD(sim2.execute());
ASSERT_EQUALS(CD == outputCD, true);


// test illegal
Similarity sim3(B,A,58); // B - Singular
Similarity sim4(D,C,69); // D - Singular
Similarity sim5(A,C,99); // different dimensions
Matrix E = initMatrix(2,3,1.0,2.0,3.0,4.0,5.0,6.0);
Matrix F = initMatrix(3,2,2.0,3.0,4.0,5.0,6.0,7.0);
Similarity sim6(E,A,45); // E - Not square
Similarity sim7(A,E,44); // E - Not square
Similarity sim8(E,F,43); // E,F - Not square
// try to execute them, should fail
ASSERT_THROW(CalculationServer::OperationFailed,sim3.execute());
ASSERT_THROW(CalculationServer::OperationFailed,sim4.execute());
ASSERT_THROW(CalculationServer::OperationFailed,sim5.execute());
ASSERT_THROW(CalculationServer::OperationFailed,sim6.execute());
ASSERT_THROW(CalculationServer::OperationFailed,sim7.execute());
ASSERT_THROW(CalculationServer::OperationFailed,sim8.execute());


return true;
}


bool testSolve(){
// init matrices
Matrix A = initMatrix(2,2,1.5,2.5,3.5,4.5);
Matrix B = initMatrix(2,1,5.5,6.5);
Matrix C = initMatrix(4,4,
2.5, -6.5,   4.4,  0.5,
2.2,  3.3,  0.14, 22.1,
9.2, 1.05,  22.2, 58.0,
3.6,  2.2, 0.587,  2.2);
Matrix D = initMatrix(4,1,5.5,8.5,-1.0,5.0);


// test creation
Solve solve1(A,B,50);
Solve solve2(C,D,65);
Matrix outputAB = initMatrix(2,1,-4.25, 4.75);
Matrix outputCD = initMatrix(4,1, 2.210749691138920,
-1.211866751872450,
-1.836949005065760,
0.357135014542198);


// test execute
ASSERT_NO_THROW(solve1.execute());
Matrix AB(solve1.execute());
ASSERT_EQUALS(AB == outputAB, true);
ASSERT_NO_THROW(solve2.execute());
Matrix CD(solve2.execute());
ASSERT_EQUALS(CD == outputCD, true);


// test illegal
Solve solve3(B,A,12); // wrong dimensions
Solve solve4(D,C,10); // wrong dimensions
Solve solve5(A,D,47); // wrong dimensions
Solve solve6(C,B,79); // wrong dimensions
Matrix E = initMatrix(2,2,1.0,1.0,2.0,2.0);
Solve solve7(E,B,98); // E - Singular
Matrix F = initMatrix(2,3,1.0,2.0,3.0,4.0,5.0,6.0);
Matrix G = initMatrix(3,2,2.0,3.0,4.0,5.0,6.0,7.0);
Solve solve8(F,B,41); // F - Not square
Solve solve9(G,B,2); // G - Not square
// try to execute them, should fail
ASSERT_THROW(CalculationServer::OperationFailed,solve3.execute());
ASSERT_THROW(CalculationServer::OperationFailed,solve4.execute());
ASSERT_THROW(CalculationServer::OperationFailed,solve5.execute());
ASSERT_THROW(CalculationServer::OperationFailed,solve6.execute());
ASSERT_THROW(CalculationServer::OperationFailed,solve7.execute());
ASSERT_THROW(CalculationServer::OperationFailed,solve8.execute());
ASSERT_THROW(CalculationServer::OperationFailed,solve9.execute());


return true;
}
*/

int main(){
RUN_TEST(testAddJob);
RUN_TEST(testRemoveJob);
RUN_TEST(testChangePriority);
RUN_TEST(testExecute);
RUN_TEST(testNextJob);
RUN_TEST(testTotalJobs);
/*RUN_TEST(testPoly);
RUN_TEST(testSimilarity);
RUN_TEST(testSolve);
*/

return (EXIT_SUCCESS);
}
